<template>
  <div style="display: flex;">
    <!-- 左侧PDF -->
    <div style="flex: 1; overflow-y: auto; height: 100vh;" ref="pdfContainer">
      <div v-for="(item, idx) in numPages" :key="idx">
        <canvas :ref="el => setCanvasRef(el, idx)"></canvas>
      </div>
    </div>
    <!-- 右侧高亮结果列表 -->
    <div style="width: 300px; border-left: 1px solid #eee; padding: 10px;">
      <div style="margin-bottom: 10px;">
        <input v-model="pageInput" type="number" min="1" :max="numPages" style="width:60px" placeholder="页码" />
        <input v-model="keyword" placeholder="输入关键词" />
        <button @click="highlightKeyword">高亮</button>
      </div>
      <div v-if="highlightList.length">
        <div v-for="(item, i) in highlightList" :key="i" style="margin-bottom: 8px; cursor: pointer; color: #409eff;"
          @click="scrollToHighlight(item)">
          <span>第{{ item.page + 1 }}页</span>：{{ keyword }}
        </div>
      </div>
      <div v-else style="color: #aaa;">无高亮结果</div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue'
import * as pdfjsLib from 'pdfjs-dist/build/pdf'

pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.js'

const keyword = ref('')
const pageInput = ref(1) // 新增页码输入，默认为1
const numPages = ref(0)
const canvasEls = ref([])
const pdfContainer = ref(null)
let pdfDoc = null
let viewports = []
let textContents = []
const highlightList = ref([]) // {page, charIdx, y}

function setCanvasRef(el, idx) {
  if (el) {
    canvasEls.value[idx] = el
  }
}

function normalizeTextWithMap(str, charMap) {
  // 返回去除空白后的字符串和新charMap
  let newStr = ''
  let newMap = []
  for (let i = 0; i < str.length; i++) {
    if (!/\s|　/.test(str[i])) {
      newStr += str[i]
      newMap.push(charMap[i])
    }
  }
  return { newStr, newMap }
}

function getAllHighlightCharRects(items, keyword, viewport, pageIdx) {
  // 1. 构建原始 charMap 和 fullText
  const charMap = []
  let fullText = ''
  items.forEach((item, itemIndex) => {
    for (let i = 0; i < item.str.length; i++) {
      charMap.push({ itemIndex, charIndexInItem: i })
      fullText += item.str[i]
    }
  })

  // 2. 归一化文本和 charMap
  const { newStr: normFullText, newMap: normCharMap } = normalizeTextWithMap(fullText, charMap)
  const normKeyword = keyword.replace(/\s|　/g, '')

  // 3. 查找并高亮
  const rects = []
  const anchorRects = [] // 只存每个关键词的首字锚点
  let pos = 0
  while (true) {
    const idx = normFullText.indexOf(normKeyword, pos)
    if (idx === -1) break
    // 高亮每个字
    for (let k = 0; k < normKeyword.length; k++) {
      const { itemIndex, charIndexInItem } = normCharMap[idx + k]
      const item = items[itemIndex]
      const totalWidth = item.width
      const avgWidth = totalWidth / item.str.length
      const x1 = item.transform[4] + avgWidth * charIndexInItem
      const y1 = item.transform[5]
      const x2 = x1 + avgWidth
      const y2 = item.transform[5] + item.height
      const rect = viewport.convertToViewportRectangle([x1, y1, x2, y2])
      const x = rect[0]
      const y = rect[3]
      const width = rect[2] - rect[0]
      const height = rect[1] - rect[3]
      rects.push({
        page: pageIdx,
        charIdx: idx + k,
        x, y, width, height
      })
      // 只在第一个字时，push到 anchorRects
      if (k === 0) {
        anchorRects.push({
          page: pageIdx,
          charIdx: idx,
          x, y, width, height
        })
      }
    }
    pos = idx + 1
  }
  return { rects, anchorRects }
}

async function renderAllPagesAndHighlight() {
  highlightList.value = []
  for (let i = 0; i < pdfDoc.numPages; i++) {
    const page = await pdfDoc.getPage(i + 1)
    const viewport = page.getViewport({ scale: 1.5 })
    viewports[i] = viewport
    const textContent = await page.getTextContent()
    textContents[i] = textContent

    await nextTick()
    const canvas = canvasEls.value[i]
    if (!canvas) continue
    canvas.height = viewport.height
    canvas.width = viewport.width
    const context = canvas.getContext('2d')
    await page.render({
      canvasContext: context,
      viewport: viewport
    }).promise

    // 只在指定页高亮
    if (
      keyword.value &&
      textContent &&
      i === Number(pageInput.value) - 1 // 页码输入为1开始，i为0开始
    ) {
      context.save()
      context.globalAlpha = 0.4
      context.fillStyle = 'yellow'
      const { rects, anchorRects } = getAllHighlightCharRects(textContent.items, keyword.value, viewport, i)
      rects.forEach(rect => {
        context.fillRect(rect.x, rect.y, rect.width, rect.height)
      })
      highlightList.value.push(...anchorRects)
      context.restore()
    }
  }
}

onMounted(async () => {
  const loadingTask = pdfjsLib.getDocument('/02.pdf')
  pdfDoc = await loadingTask.promise
  numPages.value = pdfDoc.numPages
  await renderAllPagesAndHighlight()
})

function highlightKeyword() {
  renderAllPagesAndHighlight()
}

// 滚动到对应 canvas 并描点到高亮区域
function scrollToHighlight(item) {
  const canvas = canvasEls.value[item.page]
  if (!canvas) return
  // 计算目标canvas在容器内的偏移
  const container = pdfContainer.value
  const rect = canvas.getBoundingClientRect()
  const containerRect = container.getBoundingClientRect()
  // 目标高亮区域的 y 坐标
  const scrollTop = container.scrollTop + (rect.top - containerRect.top) + item.y - 20
  container.scrollTo({ top: scrollTop, behavior: 'smooth' })
}
</script> 